#include "svnqualitymeter.h"
#include "ui_svnqualitymeter.h"
#include <QDebug>
#include <QtXml>
#include <QtNetwork/QNetworkProxy>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <qjson/parser.h>
#include <qjson/serializer.h>


#include <QMessageBox>

using namespace QJson;


SVNQualityMeter::SVNQualityMeter(QWidget *parent) :
	QMainWindow(parent),
	ui(new Ui::SVNQualityMeter)
{
	//    QNetworkProxy proxy(QNetworkProxy::HttpProxy, "proxy.he-arc.ch", 8080);
	//    QNetworkProxy::setApplicationProxy(proxy);

	ui->setupUi(this);
	ui->repoName->setText("http://projet-compilateur.googlecode.com/svn/trunk/");
	ui->resultView->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
	ui->resultView->settings()->setAttribute(QWebSettings::JavascriptEnabled, true);
	ui->resultView->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
	ui->resultView->settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, true);

	project = new ProjectStats();

}

SVNQualityMeter::~SVNQualityMeter()
{
	delete ui;
}


void SVNQualityMeter::on_btnAnalyse_clicked()
{
	bool useLogFiles = ui->useLogFile->isChecked();

	connect(&svnLogProcess, SIGNAL(finished(int)), this, SLOT(svnLogProcessFinished()));
	connect(&svnDiffProcess, SIGNAL(finished(int)), this, SLOT(svnDiffProcessFinished()));


	QString fileName = QString(qApp->applicationDirPath()+"/svnLogQt.xml");
	svnLogProcess.setStandardOutputFile(fileName);

	// Start process svn log
	if (! useLogFiles) {
		svnLogProcess.start("svn log --xml "+ui->repoName->text());
		svnLogProcess.waitForFinished();
	}

	QFile file2(fileName);
	commitPerUser(&file2);

	// Start process svn diff for each revision
	for (int i=1; i< revisionMapping.size(); i++) {
		// svn diff -r oldRev:newRev repoName
		QString cmdName = QString("svn diff -r %2:%1 %3").arg(revisionMapping.at(i-1)).arg(revisionMapping.at(i)).arg(ui->repoName->text());
		fileName = QString(qApp->applicationDirPath()+"/svnDiff%1.xml").arg(revisionMapping.at(i));

		svnDiffProcess.setStandardOutputFile(fileName);
		diffFile = new QFile(fileName);
		if (! useLogFiles) {
			svnDiffProcess.start(cmdName);
			svnDiffProcess.waitForFinished();
		}
		countLines(diffFile);
	}

	QMessageBox::information(this,"Analyse finished","The analyse is finished !");

	// Display user info
	for (int i=0; i<authorList.size(); i++) {
		authorList.values()[i]->avgAddedLine /= authorList.values()[i]->commitNumber;
		authorList.values()[i]->avgRemovedLine /= authorList.values()[i]->commitNumber;
		authorList.values()[i]->displayInfo();
	}

}


void SVNQualityMeter::svnLogProcessFinished() {
	qDebug()<< "Logdone";
}
void SVNQualityMeter::svnDiffProcessFinished() {
	qDebug()<< "Diffdone";
}

void SVNQualityMeter::countLines(QFile *filename) {
	// Start count lines
	int countAddedLines = 0;
	int countRemovedLines = 0;

	if (!filename->open(QIODevice::ReadOnly))
		return;

	// Read all the file
	int revApparition = 0;
	int revEnd;
	while (!filename->atEnd()) {
		QString line = filename->readLine();
		// Check the revision nummer
		if (revApparition < 2 && line.contains("revision")) {
			// Retrieve the revNr (last number of the line)
			QString tmpString = line.mid(line.lastIndexOf(' ')+1, line.size()-line.lastIndexOf(' ')-3);
			if (revApparition == 1) {
				revEnd = tmpString.toInt();
			}
			revApparition ++;
		}
		if ((line.size()>1 && line.at(0) == '+' && line.at(1)!='+') || (line.size() == 1 && line.at(0) == '+')) {
			countAddedLines ++;

		}
		if ((line.size()>1 && line.at(0) == '-' && line.at(1)!='-') || (line.size() == 1 && line.at(0) == '-')){
			countRemovedLines++;
		}
	}

	// Update author stats
	if (revisionMapping.contains(revEnd)) {
		int indexRev = revisionMapping.indexOf(revEnd);
		revisionList[indexRev]->setLinesAdded(countAddedLines);
		revisionList[indexRev]->setLinesRemoved(countRemovedLines);
		indexRev = authorMapping.indexOf(revisionList[indexRev]->author);
		authorList.values()[indexRev]->lineAdded += countAddedLines;
		authorList.values()[indexRev]->lineRemoved += countRemovedLines;
		authorList.values()[indexRev]->maxAddedLine = qMax(countAddedLines,authorList.values()[indexRev]->maxAddedLine);
		authorList.values()[indexRev]->minAddedLine = qMin(countAddedLines,authorList.values()[indexRev]->minAddedLine);
		authorList.values()[indexRev]->maxRemovedLine = qMax(countRemovedLines,authorList.values()[indexRev]->maxRemovedLine);
		authorList.values()[indexRev]->minRemovedLine = qMin(countRemovedLines,authorList.values()[indexRev]->minRemovedLine);
		authorList.values()[indexRev]->avgAddedLine += countAddedLines;
		authorList.values()[indexRev]->avgRemovedLine += countRemovedLines;
	}

}

void SVNQualityMeter::commitPerUser(QFile *filename) {

	QString tmpAuthor, tmpDate;
	int tmpRevNr, tmpMessageSize;

	int cptComment;
	int averageCommentSize;
	int maxCommentSize, minCommentSize;

	long long int tmpOldDate;
	long long int dateInterval, tmpInterval;
	long long int maxInterval, minInterval;

	// Construct a xml tree from the files
	QDomDocument doc("mydocument");
	if (!filename->open(QIODevice::ReadOnly))
		return;
	if (!doc.setContent(filename)) {
		filename->close();
		return;
	}
	filename->close();


	// Retrieve data from xml file
	QDomNode n = doc.documentElement().firstChild();
	while(!n.isNull()) {
		QDomElement e = n.toElement();
		if (!e.isNull() && e.tagName() == "logentry") {
			tmpRevNr = e.attribute("revision").toInt();
			QDomElement grandChild=e.firstChild().toElement();  // Browse the child of logentry node
			while(!grandChild.isNull()) {
				if (grandChild.tagName() == "author") { // Retrieve author
					tmpAuthor = grandChild.text();
					// Update / Create authors stats
					if (authorList.contains(tmpAuthor)) {
						authorList.value(tmpAuthor)->commitNumber++;
					} else {
						authorList.insert(tmpAuthor, new AuthorStats(tmpAuthor, 1));
						authorMapping.append(tmpAuthor);
					}
				} else if (grandChild.tagName() == "msg") { // Retrieve comment size
					tmpMessageSize = grandChild.text().size();
				} else if (grandChild.tagName() == "date") { // Retrieve commit date
					tmpDate = grandChild.text();
				}
				grandChild = grandChild.nextSibling().toElement();
			}
			// Append the revision info to this list
			revisionList.append(new RevisionEntry(tmpRevNr, tmpAuthor, tmpMessageSize, tmpDate));
			revisionMapping.append(tmpRevNr);
		}
		n = n.nextSibling();
	}

	// Average / max / min comment size / user
	averageCommentSize = 0;cptComment = 0;
	maxCommentSize = 0;	minCommentSize = INT_MAX;
	for (int i=0; i<revisionList.size(); i++) {
		maxCommentSize = qMax(maxCommentSize, revisionList.at(i)->messageSize);
		minCommentSize = qMin(minCommentSize, revisionList.at(i)->messageSize);
		averageCommentSize += revisionList.at(i)->messageSize;
	}
	averageCommentSize /= revisionList.size();
	project->avgCommentSize = averageCommentSize;
	project->maxCommentSize = maxCommentSize;
	project->minCommentSize = minCommentSize;



	// Average / max / min comment size / user
	for (int j=0; j<authorList.size(); j++) {
		averageCommentSize = 0;cptComment = 0;
		maxCommentSize = 0;	minCommentSize = INT_MAX;
		for (int i=0; i<revisionList.size(); i++) {
			if (revisionList[i]->author == authorList.keys()[j]) {
				maxCommentSize = qMax(maxCommentSize, revisionList.at(i)->messageSize);
				minCommentSize = qMin(minCommentSize, revisionList.at(i)->messageSize);
				averageCommentSize += revisionList.at(i)->messageSize;
				cptComment++;
			}
		}

		averageCommentSize /= cptComment; //Minimum cptComment equal 1
		authorList.values()[j]->avgCommentSize = averageCommentSize;
		authorList.values()[j]->maxCommentSize = maxCommentSize;
		authorList.values()[j]->minCommentSize = minCommentSize;
	}

	// Average / Max / Min commit frequency / user
	for (int j=0; j<authorList.size(); j++) {
		// Check that author has at least 2 commits
		if (authorList.values()[j]->commitNumber >= 2) {
			dateInterval = 0;
			maxInterval=0, minInterval= LONG_LONG_MAX;

			// Search the first appearance of the author
			int i=0;
			while ((i<revisionList.size()) && revisionList[i]->author != authorList.keys()[j]) {
				i++;
			}

			// Process the max/min/average commit interval
			tmpOldDate = revisionList[i]->dateTime.toMSecsSinceEpoch();

			for (int k=i+1; k<revisionList.size(); k++) {
				if (revisionList[k]->author == authorList.keys()[j]) {
					tmpInterval = tmpOldDate - revisionList[k]->dateTime.toMSecsSinceEpoch();

					maxInterval = qMax(maxInterval, tmpInterval);
					minInterval = qMin(minInterval, tmpInterval);

					dateInterval += tmpOldDate - revisionList[k]->dateTime.toMSecsSinceEpoch();
					tmpOldDate = revisionList[k]->dateTime.toMSecsSinceEpoch();
				}
			}
			maxInterval /= 1000; minInterval /= 1000;
			dateInterval = dateInterval / (1000*authorList.values()[j]->commitNumber);

			authorList.values()[j]->avgCommitFreq = dateInterval;
			authorList.values()[j]->maxCommitFreq = maxInterval;
			authorList.values()[j]->minCommitFreq = minInterval;
		}
	}
}

void SVNQualityMeter::displayResults(void) {
	// Send data to the webpage in QueryString
	qDebug() << "PASS";
	ui->resultView->load(QUrl("qrc:///html/test.html?{\"bindings\": [1,2,3]}"));//qrc:///html/Gauge.html?Mem=44&CPU=88&Network=11
	ui->resultView->show();
}

void SVNQualityMeter::on_btnGraphe1_clicked() {
	Serializer serializer;
	qDebug()<<"Affiche Graphe 1 ";

	QVariantList devscommits;

	for (int i=0; i<authorList.size(); i++) {
		QVariantMap tmp;
		tmp.insert("Nom",authorList.values()[i]->name);
		tmp.insert("NombreCommits", authorList.values()[i]->commitNumber);
		devscommits.append(tmp);
	}


	QByteArray json = serializer.serialize(devscommits);
	qDebug()<< json.size();
	ui->resultView->load(QUrl("qrc:///html/graphe1.html?"+json));
	ui->resultView->show();

}

// Lines comment size / commit
void SVNQualityMeter::on_btnGraphe2_clicked(){
	qDebug()<<"Affiche Graphe 2";
	Serializer serializer;

	QVariantMap commentsSize;
	QVariantList commentRevesion;


	commentsSize.insert("min",project->minCommentSize);
	commentsSize.insert("max",project->maxCommentSize);
	commentsSize.insert("avg",project->avgCommentSize);

	for (int i=revisionList.size()-1; i>= 0; i--) {
		QVariantMap tmp;
		tmp.insert("x", "rev " + QString::number(revisionList.at(i)->revNr));
		tmp.insert("commitSize", revisionList.at(i)->messageSize);
		commentRevesion.append(tmp);
	}
	commentsSize.insert("revisions",commentRevesion);

	QByteArray json = serializer.serialize(commentsSize);
	qDebug()<< json.size();
	ui->resultView->load(QUrl("qrc:///html/graphe2.html?"+json));
	ui->resultView->show();
}


// Lines added - removed - total / commit
void SVNQualityMeter::on_btnGraphe3_clicked(){
	qDebug()<<"Affiche Graphe 3";
	Serializer serializer;

	QVariantList linesStats;

	for (int i=revisionList.size()-1; i>= 0; i--) {
		QVariantMap tmp;
		tmp.insert("x", "rev " + QString::number(revisionList.at(i)->revNr));
		tmp.insert("linesAdded", revisionList.at(i)->linesAdded);
		tmp.insert("linesRemoved", revisionList.at(i)->linesRemoved);
		tmp.insert("linesTotal", revisionList.at(i)->linesAdded - revisionList.at(i)->linesRemoved);
		linesStats.append(tmp);
	}

	QByteArray json = serializer.serialize(linesStats);
	qDebug()<< json.size();
	ui->resultView->load(QUrl("qrc:///html/graphe3.html?"+json));
	ui->resultView->show();
}

void SVNQualityMeter::on_btnGraphe4_clicked(){
	qDebug()<< "Affiche Graphe 4";
	Serializer serializer;

	QVariantList devscommits;


	for (int i=0; i<authorList.size(); i++) {
		QVariantMap tmp;
		tmp.insert("Nom",authorList.values()[i]->name);
		tmp.insert("avgCommentSize", authorList.values()[i]->avgCommentSize);
		tmp.insert("minCommentSize", authorList.values()[i]->minCommentSize);
		tmp.insert("maxCommentSize", authorList.values()[i]->maxCommentSize);
		devscommits.append(tmp);
	}

	//qDebug()<< serializer.serialize(devscommits);

	QByteArray json = serializer.serialize(devscommits);
	qDebug()<< json.size();
	ui->resultView->load(QUrl("qrc:///html/graphe4.html?"+json));
	ui->resultView->show();

}

void SVNQualityMeter::on_btnGraphe5_clicked()
{
    qDebug()<<"Affiche Graphe 5";
	Serializer serializer;

	QVariantMap commentStats;
    QHash<QString, QList<RevisionEntry *> > listRevPerAuthor;
    int maxListSize = 0;
    // for each user
    for (int i=0; i< authorMapping.size(); i++) {
        // build a list of its revision
        QString tmpAuthor = authorMapping.at(i);
        QList<RevisionEntry *> tmp;
        for (int j=0; j< revisionList.size(); j++) {
            if (revisionList.at(j)->author == tmpAuthor) {
                tmp.append(revisionList.at(j));
            }
        }
        listRevPerAuthor.insert(tmpAuthor, tmp);
        maxListSize = qMax(maxListSize, tmp.size());
    }
	
    QVariantList authors;
    for (int i=0; i<listRevPerAuthor.size(); i++) {
        QVariantMap tmp4;
        tmp4.insert("name", listRevPerAuthor.keys()[i]);
        authors.append(tmp4);
    }
    commentStats.insert("authors", authors);
	
    QVariantList tmp2;
    // Longueur du message de commit / dev
    for (int i=0; i<maxListSize; i++) {
        QVariantMap tmp;
        for (int j=0;j<listRevPerAuthor.size();j++) {
            // for each user take the ième rev stat if available
            if (i < listRevPerAuthor.values()[j].size()) {
                tmp.insert(listRevPerAuthor.keys()[j], listRevPerAuthor.values()[j].at(i)->messageSize);
            } else {
                tmp.insert(listRevPerAuthor.keys()[j], "null");
            }
        }
        tmp2.append(tmp);
    }
    commentStats.insert("revisions", tmp2);
    QByteArray json = serializer.serialize(commentStats);
	
    ui->resultView->load(QUrl("qrc:///html/graphe5.html?"+json));
    ui->resultView->show();
}
void SVNQualityMeter::on_btnGraphe6_clicked()
{
	qDebug()<< "Affiche Graphe 6";
	Serializer serializer;

	QVariantList devsCommitFreq;


	for (int i=0; i<authorList.size(); i++) {
		if(authorList.values()[i]->commitNumber>1){
			QVariantMap tmp;
			tmp.insert("Nom",authorList.values()[i]->name);
			tmp.insert("avgCommitFreq", authorList.values()[i]->avgCommitFreq/3600);
			tmp.insert("minCommitFreq", authorList.values()[i]->minCommitFreq/3600);
			tmp.insert("maxCommitFreq", authorList.values()[i]->maxCommitFreq/3600);
			devsCommitFreq.append(tmp);
		}
	}

	QByteArray json = serializer.serialize(devsCommitFreq);
	ui->resultView->load(QUrl("qrc:///html/graphe6.html?"+json));
	ui->resultView->show();

}

void SVNQualityMeter::on_btnGraphe7_clicked()
{
	qDebug()<< "Affiche Graphe 7";
	Serializer serializer;

	QVariantList devsAddedLine;


	for (int i=0; i<authorList.size(); i++) {
			QVariantMap tmp;
			tmp.insert("Nom",authorList.values()[i]->name);
			tmp.insert("avgAddedLine", authorList.values()[i]->avgAddedLine);
			tmp.insert("minAddedLine", authorList.values()[i]->minAddedLine);
			tmp.insert("maxAddedLine", authorList.values()[i]->maxAddedLine);
			devsAddedLine.append(tmp);
	}

	QByteArray json = serializer.serialize(devsAddedLine);
	ui->resultView->load(QUrl("qrc:///html/graphe7.html?"+json));
	ui->resultView->show();

}

void SVNQualityMeter::on_btnGraphe8_clicked()
{
	qDebug()<< "Affiche Graphe 8";
	Serializer serializer;

	QVariantList devsRemovedLine;


	for (int i=0; i<authorList.size(); i++) {
			QVariantMap tmp;
			tmp.insert("Nom",authorList.values()[i]->name);
			tmp.insert("avgRemovedLine", authorList.values()[i]->avgRemovedLine);
			tmp.insert("minRemovedLine", authorList.values()[i]->minRemovedLine);
			tmp.insert("maxRemovedLine", authorList.values()[i]->maxRemovedLine);
			devsRemovedLine.append(tmp);
	}

	QByteArray json = serializer.serialize(devsRemovedLine);
	ui->resultView->load(QUrl("qrc:///html/graphe8.html?"+json));
	ui->resultView->show();

}

void SVNQualityMeter::on_btnGraphe9_clicked()
{
    qDebug()<<"Affiche Graphe 9";
    Serializer serializer;

    QVariantMap commentStats;
    QHash<QString, QList<RevisionEntry *> > listRevPerAuthor;
    int maxListSize = 0;
    // for each user
    for (int i=0; i< authorMapping.size(); i++) {
        // build a list of its revision
        QString tmpAuthor = authorMapping.at(i);
        QList<RevisionEntry *> tmp;
        for (int j=0; j< revisionList.size(); j++) {
            if (revisionList.at(j)->author == tmpAuthor) {
                tmp.append(revisionList.at(j));
            }
        }
        listRevPerAuthor.insert(tmpAuthor, tmp);
        maxListSize = qMax(maxListSize, tmp.size());
    }

    QVariantList authors;
    for (int i=0; i<listRevPerAuthor.size(); i++) {
        QVariantMap tmp4;
        tmp4.insert("name", listRevPerAuthor.keys()[i]);
        authors.append(tmp4);
    }
    commentStats.insert("authors", authors);

    QVariantList tmp2;
    // Longueur du message de commit / dev
    for (int i=0; i<maxListSize; i++) {
        QVariantMap tmp;
        for (int j=0;j<listRevPerAuthor.size();j++) {
            // for each user take the ième rev stat if available
            if (i < listRevPerAuthor.values()[j].size()) {
                tmp.insert(listRevPerAuthor.keys()[j], listRevPerAuthor.values()[j].at(i)->linesAdded);
            } else {
                tmp.insert(listRevPerAuthor.keys()[j], "null");
            }
        }
        tmp2.append(tmp);
    }
    commentStats.insert("revisions", tmp2);
    QByteArray json = serializer.serialize(commentStats);

    ui->resultView->load(QUrl("qrc:///html/graphe9.html?"+json));
    ui->resultView->show();

}

void SVNQualityMeter::on_btnGraphe10_clicked()
{
	qDebug()<<"Affiche Graphe 10";
		Serializer serializer;

		QVariantMap commentStats;
		QHash<QString, QList<RevisionEntry *> > listRevPerAuthor;
		int maxListSize = 0;
		// for each user
		for (int i=0; i< authorMapping.size(); i++) {
			// build a list of its revision
			QString tmpAuthor = authorMapping.at(i);
			QList<RevisionEntry *> tmp;
			for (int j=0; j< revisionList.size(); j++) {
				if (revisionList.at(j)->author == tmpAuthor) {
					tmp.append(revisionList.at(j));
				}
			}
			listRevPerAuthor.insert(tmpAuthor, tmp);
			maxListSize = qMax(maxListSize, tmp.size());
		}

		QVariantList authors;
		for (int i=0; i<listRevPerAuthor.size(); i++) {
			QVariantMap tmp4;
			tmp4.insert("name", listRevPerAuthor.keys()[i]);
			authors.append(tmp4);
		}
		commentStats.insert("authors", authors);

		QVariantList tmp2;
		// Nombre de lignes supprimée par commit / dev
		for (int i=0; i<maxListSize; i++) {
			QVariantMap tmp;
			for (int j=0;j<listRevPerAuthor.size();j++) {
				// for each user take the ième rev stat if available
				if (i < listRevPerAuthor.values()[j].size()) {
					tmp.insert(listRevPerAuthor.keys()[j], listRevPerAuthor.values()[j].at(i)->linesRemoved);
				} else {
					tmp.insert(listRevPerAuthor.keys()[j], "null");
				}
			}
			tmp2.append(tmp);
		}
		commentStats.insert("revisions", tmp2);
		QByteArray json = serializer.serialize(commentStats);

		ui->resultView->load(QUrl("qrc:///html/graphe10.html?"+json));
		ui->resultView->show();

}

void SVNQualityMeter::on_btnGraphe11_clicked()
{
    qDebug()<<"Affiche Graphe 11";
        Serializer serializer;

        QVariantMap commentStats;
        QHash<QString, QList<RevisionEntry *> > listRevPerAuthor;
        int maxListSize = 0;
        // for each user
        for (int i=0; i< authorMapping.size(); i++) {
            // build a list of its revision
            QString tmpAuthor = authorMapping.at(i);
            QList<RevisionEntry *> tmp;
            for (int j=0; j< revisionList.size(); j++) {
                if (revisionList.at(j)->author == tmpAuthor) {
                    tmp.append(revisionList.at(j));
                }
            }
            listRevPerAuthor.insert(tmpAuthor, tmp);
            maxListSize = qMax(maxListSize, tmp.size());
        }

        QVariantList authors;
        for (int i=0; i<listRevPerAuthor.size(); i++) {
            QVariantMap tmp4;
            tmp4.insert("name", listRevPerAuthor.keys()[i]);
            authors.append(tmp4);
        }
        commentStats.insert("authors", authors);

        QVariantList tmp2;
        // Longueur du message de commit / dev
        for (int i=0; i<maxListSize; i++) {
            QVariantMap tmp;
            for (int j=0;j<listRevPerAuthor.size();j++) {
                // for each user take the ième rev stat if available
                if (i < listRevPerAuthor.values()[j].size()) {
                    tmp.insert(listRevPerAuthor.keys()[j], listRevPerAuthor.values()[j].at(i)->linesAdded - listRevPerAuthor.values()[j].at(i)->linesRemoved);
                } else {
                    tmp.insert(listRevPerAuthor.keys()[j], "null");
                }
            }
            tmp2.append(tmp);
        }
        commentStats.insert("revisions", tmp2);
        QByteArray json = serializer.serialize(commentStats);

        ui->resultView->load(QUrl("qrc:///html/graphe11.html?"+json));
        ui->resultView->show();


}
